package br.ufpb.di.ppgi.negocio.servicos;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import br.com.caelum.vraptor.ioc.Component;
import br.ufpb.di.ppgi.negocio.iservicos.IPropostaService;
import br.ufpb.di.ppgi.negocio.modelo.homologacao.proposta.HomologacaoProposta;
import br.ufpb.di.ppgi.negocio.modelo.homologacao.proposta.ResultadoHomologacaoProposta;
import br.ufpb.di.ppgi.negocio.modelo.proposta.Proposta;
import br.ufpb.di.ppgi.negocio.modelo.proposta.PropostaStatus;
import br.ufpb.di.ppgi.negocio.modelo.usuario.Usuario;
import br.ufpb.di.ppgi.negocio.modelo.usuario.UsuarioRole;
import br.ufpb.di.ppgi.persistencia.QueryLocator;
import br.ufpb.di.ppgi.persistencia.domainstore.DomainStore;
import br.ufpb.di.ppgi.persistencia.pagination.ListaPaginada;
import br.ufpb.di.ppgi.util.logger.Logger;
import br.ufpb.di.ppgi.util.logger.LoggerFactory;

@Component
public class PropostaService implements IPropostaService {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(PropostaService.class);
		
	private final DomainStore ds;
	private final QueryLocator queries;
	
	public PropostaService(DomainStore ds, QueryLocator queries) {
		this.ds = ds;
		this.queries = queries;
	}

	
	private List<Usuario> getColegiadosEmVigencia() {
		List<UsuarioRole> roles = new ArrayList<UsuarioRole>();
		roles.add(UsuarioRole.COLEGIADO);
		roles.add(UsuarioRole.COORDENADOR);
		
		return queries.forUsuario().listByRoles(roles);
	}
	
	@Override
	public void adicionar(Proposta proposta) {
		//TODO Adicionar 5 entradas em HOMOLOGACAO
		
		List<Usuario> colegiados = getColegiadosEmVigencia();
		
		proposta.setColegiados(colegiados);
		ds.add(proposta);
		
		for (Usuario usuario : colegiados) {
			ds.add(new HomologacaoProposta(proposta, usuario));
		}
		//TODO Pra quando tiver colocando mais do que 2 professores
//		List<Usuario> professores = proposta.getProfessores();
//		for (Usuario usuario : professores) {
//			ds.add(new ParticipaProposta(proposta, usuario));
//		}		
	}


	@Override
	public Proposta apagar(Long id) {
		//TODO Implementar metodo APAGAR propostas ?
		LOGGER.debug("Metodo nao implementado.");
		return null;
	}

	@Override
	public Proposta atualizar(Proposta proposta) {
		return ds.merge(proposta);
	}

	@Override
	public Proposta buscar(Long id) {
		return ds.get(Proposta.class, id);
	}

	@Override
	public List<Proposta> listar() {
		return ds.list(Proposta.class);
	}

	@Override
	public List<Proposta> listarByUsuarioId(Long id) {
		return queries.forProposta().listByOrientadorIdAtivo(id);
	}


	@Override
	public List<Proposta> listarPendencias(Usuario usuario) {
		return queries.forProposta().listPendenciasByUsuario(usuario);
	}
	
	public List<Proposta> listarPendencias(Long id) {
		return queries.forProposta().listPendenciasByUsuario(queries.forUsuario().get(id));
	}


	@Override
	public boolean homologar(HomologacaoProposta homologacao, boolean homologado) {
		homologacao = ds.get(HomologacaoProposta.class, homologacao.getPk());
		homologacao.setHomologado(homologado);
		
		homologacao = ds.merge(homologacao);

		Proposta proposta = homologacao.getPk().getProposta();
		ResultadoHomologacaoProposta resultadoHomologacao = calcularResultado(proposta);
		
		if (resultadoHomologacao.isEncerrado()) {
			proposta = ds.get(Proposta.class, proposta.getId());
			proposta.setStatus(resultadoHomologacao.getResultado());
			proposta = ds.merge(proposta);
			fechaHomologacao(proposta);
		}
		
		return homologacao.isHomologado();
	}

	private void fechaHomologacao(Proposta proposta) {
		List<HomologacaoProposta> homologacoes = queries.forProposta().listPendencias(proposta);
		for(HomologacaoProposta homologacao : homologacoes) {
			homologacao.setHomologado(false);
			ds.merge(homologacao);
		}
	}

	private ResultadoHomologacaoProposta calcularResultado(Proposta proposta) {
		int metadeDosVotos = queries.forProposta().countTotalVotos(proposta)/2;
		
		if (queries.forProposta().isVotacaoInsuficiente(proposta)) 
			;
		else if (queries.forProposta().countVotosHomologacao(proposta,true) >= metadeDosVotos)
			return new ResultadoHomologacaoProposta(true,PropostaStatus.HOMOLOGADO);
		else if (queries.forProposta().countVotosHomologacao(proposta,false) >= metadeDosVotos)
			return new ResultadoHomologacaoProposta(true,PropostaStatus.HOMOLOGACAO_REPROVADA);
		
		return new ResultadoHomologacaoProposta(false,PropostaStatus.PENDENTE);
	}


	@Override
	public List<Proposta> listarByStatus(PropostaStatus pendencias) {
		return queries.forProposta().listByStatus(pendencias);
	}


	@Override
	public Boolean aprovar(Proposta proposta, Boolean aprovado) throws Exception {
		proposta = ds.get(Proposta.class, proposta.getId());
		
		if (proposta.getStatus() != PropostaStatus.HOMOLOGADO)
			throw new Exception("Não é possivel aprovar uma Proposta não homologada");
		
		if (aprovado)
			proposta.setStatus(PropostaStatus.APROVADO);
		else
			proposta.setStatus(PropostaStatus.REPROVADO);
		
		proposta = ds.merge(proposta);

		return aprovado;
	}


	@Override
	public void finalizar(Proposta proposta) {
		proposta = ds.get(Proposta.class, proposta.getId());
		
		proposta.setStatus(PropostaStatus.FINALIZADA);
		
		proposta = ds.merge(proposta);
	}


	@Override
	public void reativarByAlunoId(Long id) {
		List<Proposta> propostas = queries.forProposta().listByAlunoId(id);
		Proposta proposta = null;
		
		for (Proposta p : propostas) {
			if(p.getStatus() == PropostaStatus.FINALIZADA) {
				proposta = p;
				break;
			}
		}
		
		if(proposta != null) {
			proposta.setStatus(PropostaStatus.APROVADO);
			ds.merge(proposta);
		}
	}


	@Override
	public ListaPaginada<Proposta> filtrarPaginando(String keyword,
			Integer page, String base) {
		
		List<PropostaStatus> statusAceitaveisProposta = Arrays.asList(
																PropostaStatus.APROVADO,
																PropostaStatus.REPROVADO,
																PropostaStatus.HOMOLOGADO,
																PropostaStatus.HOMOLOGACAO_REPROVADA,
																PropostaStatus.PENDENTE
															);
		
		return queries.forProposta().paginarByKeyAndStatusList(keyword,statusAceitaveisProposta)
						.query(10, page)
						.setBase(base)
						.toVersaoEmPortugues();
	}
	
}
